import java.util.ArrayList;


public class Minimax {
	private Node givenState;
	private ArrayList<Node> children;
	private GameBoardTool gTool = new GameBoardTool();
	private static int MIN;
	private static int MAX;
	public int recursions = 0;
	private int border;

	public Minimax(GameBoard gm, int player) {
		children = new ArrayList<Node>();
		givenState = new Node(gm, children);
		border = givenState.getGameBoard().numberOfMoves(); 
		MAX = player;
		if (MAX == 1) MIN = 2;
		else MIN = 1;
	}

	public int getAction(){
		int action = -1;
		//get the utility of each action possible for max
		procreate(givenState, MAX);

		float maxUtility = 0;
		int index = 1;
		//Choose the action with the highest value
		for (Node treeNode : givenState.getChildren()) {
			if(!treeNode.isDead() ){
				treeNode.setUtility(minimize(treeNode, index));if(treeNode.getUtility() > maxUtility || maxUtility == 0){
					maxUtility = treeNode.getUtility();
					//get the index of the action, i.e. the column to play.
					action = index;
				}
			}
			index++;
		}
		return (action - 1);
	}

	private void procreate(Node fromState, int player){
		for (int i = 1; i <= fromState.getGameBoard().getNumColumns(); i++){
			GameBoard tmp = fromState.getGameBoard().peakColumn(i, player);
			if (tmp == null){
				Node child = new Node(tmp, null);
				child.setDead(true);
				fromState.setChild(child);
			}else{//if the column is full create a child with utility null
				ArrayList<Node> grandChildren = new ArrayList<Node>();
				Node child = new Node(tmp, grandChildren);
				fromState.setChild(child);
			}
		}
	}
	private float minimize(Node state, int column){
		float result = 0;
		recursions++;
		//if the column given as argument to the method is full or if we reach a depth of 7
		if(gTool.columnFull(state.getGameBoard() ,column) || state.getGameBoard().numberOfMoves() == border + 5 || gTool.isWinningColumn(state.getGameBoard(), column, MAX)){
			//return the value of the move
			return gTool.columnValue(state.getGameBoard(), column, MAX, 1);
		}	//otherwise continue the recursion to the next move
		procreate(state, MIN);
		int col = 1;
		for (Node n : state.getChildren()) {
			if(!n.isDead()){
				n.setUtility(maximize(n,col));

				if (result == 0){
					result = n.getUtility();
				}
				if(n.getUtility() < result){
					result = n.getUtility();
				}
			}
			col++;
		}
		return result;
	}
	private float maximize(Node state, int column){
		float result = 0;
		recursions++;
		//if the column given as argument to the method is full or if we reach a depth of 7
		if(gTool.columnFull(state.getGameBoard() ,column) || state.getGameBoard().numberOfMoves() == border + 5 || gTool.isWinningColumn(state.getGameBoard(), column, MIN)){
			//return the value of the move
			return gTool.columnValue(state.getGameBoard(), column, MIN, -1);
		}	//otherwise continue the recursion to the next move
		procreate(state, MAX);
		int col = 1;
		for (Node n : state.getChildren()) {
			if(!n.isDead()){
				n.setUtility(maximize(n,col));

				if (result == 0){
					result = n.getUtility();
				}
				if(n.getUtility() > result){
					result = n.getUtility();
				}
			}
			col++;
		}
		return result;
	}
}
